import { Lazy, LazyList, LazyConvert, LazyNumber, Pair, pair } from "./types"


export const compose = <T1, T2, T3>
    (f: (x: T2) => T3, g: (y: T1) => T2)
    : (x: T1) => T3 => {
    return (x: T1) => f(g(x))
}

// cons: <T>(x: Lazy<T>) => (ys: LazyList<T>) => LazyList<T>
// 生成以 x 为首元素，ys 为尾 List 的 LazyList
export const cons = <T>
    (x: Lazy<T>) =>
    (ys: LazyList<T>)
        : LazyList<T> => {
        const a = x()
        if (a === null) {
            return () => null;
        }
        return () => {
            return {
                head: () => a,
                tail: ys
            }
        }
    }

export const map = <T, S>
    (f: LazyConvert<T, S>) =>
    (xs: LazyList<T>)
        : LazyList<S> => {
        const pair = xs();
        if (pair === null) {
            return () => null;
        }
        const x = pair.head;
        const l = pair.tail
        return cons(f(x))(
            () => map(f)(l)()
        )
    }


// 生成 [x, f(x), f(f(x)), ...]
export const repeat = <T>
    (f: LazyConvert<T, T>) =>
    (x: Lazy<T>): LazyList<T> =>
        cons(x)
            (() => repeat(f)(f(x))());



export const take = <T>
    (n: LazyNumber, xs: LazyList<T>)
    : LazyList<T> =>
    () => {
        let m = n();
        let pair = xs()
        if (m === 0) {
            return null
        }
        if (pair === null) {
            return null
        }
        return {
            head: pair.head,
            tail: take(() => (m - 1), pair.tail)
        }
    }



export const zip = <T, S>
    (xs: LazyList<T>) =>
    (ys: LazyList<S>)
        : LazyList<Pair<T, S>> => {
        const p1 = xs();
        const p2 = ys();
        if (p1 === null || p2 === null) {
            return () => null;
        }

        return cons
            (() => pair<T, S>(p1.head)(p2.head)())
            (() => zip<T, S>(p1.tail)(p2.tail)())
    }